﻿
interface JQuery {
    /**
    * 获取控件类型
    * @param control
    * @returns
    */
    GetControlType(): Sail.ControlType;
    SetValue(value: any): JQuery;
    GetValue(type: string): JQuery;
    BindObject(model: any): JQuery;
}



module Sail {
    export enum ControlType {
        Val = 0, //文本
        Checked = 1,//勾选
        Src = 2,//地址
        Html = 3//内容
    };

    $.fn.GetControlType = function () {
        var control = this;
        switch (control.prop("tagName")) {
            case "IMG":
                return ControlType.Src;
            case "SELECT":
            case "TEXTAREA":
                return ControlType.Val;
            case "INPUT":
                switch (control.attr("type")) {
                    case "checkbox":
                    case "radio":
                        return ControlType.Checked;
                    default:
                        return ControlType.Val;
                }
            default:
                return ControlType.Html;
        }
    }

    $.fn.SetValue = function (value: any) {

        function formatValue(control: JQuery, value: any) {
            var helper = control.data("helper");
            if (helper) {
                var func = <Function>Sail.Binder.Helpers[helper];
                return func(value);
            }
            return value;
        }

        var control = this;
        if (control && typeof (value) != "undefined") {
            switch (control.GetControlType()) {
                case ControlType.Checked: //checkbook
                    control.prop("checked", <boolean>value);
                    if ($.fn.bootstrapSwitch && control.hasClass("checkSwitch"))
                        control.bootstrapSwitch("state", <boolean>value);
                    break;
                case ControlType.Val: //val
                    if (value || value === 0) {


                        if (control.hasClass("date") && value)
                            control.val(<string>value.substring(0, 10));
                        else if (control.hasClass("datetime") && value)
                            control.val(DateTime.Parse(<string>value).format("yyyy-MM-dd hh:mm"));
                        else if (control.data("dateformat") && value)
                            control.val(DateTime.Parse(<string>value).format(control.data("dateformat")));
                        else {
                            control.val(formatValue(control, value));
                        }
                    }
                    break;
                case ControlType.Src: //图片
                    control.prop("src", <string>value);
                    break;
                case ControlType.Html: //html标签
                    if (!value && value !== 0) control.empty();
                    else {
                        if (control.hasClass("date"))
                            control.html(<string>value.substring(0, 10));
                        else if (control.hasClass("datetime"))
                            control.html(DateTime.Parse(<string>value).format("yyyy-MM-dd hh:mm"));
                        else if (control.data("dateformat"))
                            control.html(DateTime.Parse(<string>value).format(control.data("dateformat")));
                        else
                            control.html(formatValue(control, value));
                    }
                    break;
            }
            control.trigger("change");
        }
    }

    $.fn.GetValue = function (type: string) {
        var control = this;
        var controlType = control.GetControlType();
        switch (type) {
            case "boolean":
                return control.is(":checked");
            case "number":
                return parseFloat(control.val());
            case "string":
            default:
                switch (controlType) {
                    case ControlType.Val:
                        return control.val();
                    case ControlType.Src:
                        return control.prop("src");
                    case ControlType.Html:
                        return control.html();
                    default:
                        return control.val();
                }
        }
    }

    /**
     * 获取对象的属性列表
     * @param obj
     */
    export function getProperties(obj: Object) {
        const props: Array<any> = [];
        for (let p in obj) {
            if (obj.hasOwnProperty(p)) {
                if (typeof (obj[p]) != "function")
                    props.push({ Name: p, Value: obj[p], Type: typeof (obj[p]) });
            }
        }
        return props;
    }

    /**
     * 绑定对象到表单
     * @param model 
     * @returns {} 
     */
    $.fn.BindObject = function (model: Object) {
        const $item = $(this);
        const props = getProperties(model);
        for (let index in props) {
            if (props.hasOwnProperty(index)) {
                var p = props[index];
                const control = $item.find(`#${p.Name},[name=${p.Name}]`); //找到能匹配id或name的对象
                if (control.length > 0) {
                    /**
                     * 检查有没有data-prop属性，如果有，就从目标值中获取prop指定的字段的值
                     * 常见的{a:{b:1}} 数据，会把1赋值给
                     * <select id='a' data-prop='b'> 这样的元素
                     */
                    const prop = control.data("prop");
                    let value = p.Value;
                    try {
                        if (prop) value = p.Value[prop];
                    } catch (e) {
                        value = p.Value;
                    }
                    if (value == null) value = "";
                    control.SetValue(value);

                }
                /**
                 * 如果属性是对象，而且没有赋值给指定的元素，
                 * 循环遍历所有标注了data-prop为对象名的元素，根据对象的id来赋值
                 * 例如 { a:{b:1} } 这样的数据会找到
                 * <span id='b' data-prop='a'> 这样的html元素来赋值
                */
                else if (p.Type == "object") {
                    $item.find("[data-prop='" + p.Name + "']").each(function (index, obj) {
                        var id = $(obj).prop("id");
                        try {
                            if (p.Value) this.SetValue($(obj), p.Value[id]);
                        } catch (e) {
                        }
                    });
                }
            }
        }

        /**
         * 遍历所有.prop这个class的非输入html元素，根据name赋值
         * 例如 { a:{b:1} } 这样的数据 <span name='a.b'>  就能被赋值为1
         */
        $item.find(".prop:not(input):not(select):not(textarea)").each((i, o) => {
            var $control = $(o);
            $control.empty();
            var prop = $control.attr("name");
            if (prop) {
                try {
                    var d = model;
                    var value = eval("d." + prop);
                    $control.SetValue(value);

                } catch (e) {
                    console.log(e);
                }
            }
        });

        $item.find("[data-hbind]:not(input):not(select):not(textarea)").each((i, o) => {
            var $control = $(o);
            $control.empty();
            var prop = $control.data("hbind");
            if (prop) {
                try {
                    $control.SetValue($.templates(prop).render([model]));
                } catch (e) {
                    console.log(e);
                }
            }
        });


        $item.find("[data-tmpl]:not(input):not(select):not(textarea)").each((i, o) => {
            var $control = $(o);

            var tmpl = $control.data("tmpl");
            var hdata = $control.data("hdata");

            var dd = model;
            if (hdata) dd = model[hdata];
            if (!$.isArray(dd)) dd = [dd];

            var tmplate = $control.data("template");
            if (!tmplate) {
                try {
                    tmplate = tmpl === "inline" ? $.templates($control.html()) : $.Loadtmpl(tmpl);
                    $control.data("template", tmplate);
                }
                catch (e) {
                    console.log("模板加载失败")
                }
            }
            $control.empty();
            if (tmplate) {
                try {
                    $control.html(tmplate.render(dd));
                } catch (e) {
                    console.log(e);
                }
            }
        });


        //$item.find("[data-exttmpl]:not(input):not(select):not(textarea)").each((i, o) => {
        //    var $control = $(o);
        //    $control.empty();
        //    var exttmpl = $control.data("tmpl");
        //    var hdata = $control.data("hdata");

        //    if (!tmpl) {
        //        tmpl = 
        //    }
        //    try {
        //        var dd = model;
        //        if (hdata) dd = model[hdata];
        //        $control.Render(tmpl, dd);
        //    } catch (e) {
        //        console.log(e);
        //    }

        //});

        ////获取百度编辑器的值
        //$item.setToEditor(model);
        //触发绑定时间
        $item.trigger("after.Binder", model);
    }

    export class Binder {
        private _$item: JQuery;
        public CurrentItem: Object;
        public static Helpers: any;


        public static Helper(ext: any) {
            Binder.Helpers = $.extend(Binder.Helpers, ext);
        }

        /**
         * 构造函数
         * @param {any} container 容器
         * @param {Object} objItem 默认对象 可为空
         */
        constructor(container: any, objItem?: Object) {
            this._$item = $(container);
            this.CurrentItem = objItem;
        }


        /**
         * 对象赋值到html
         * @param {Object} model
         */
        private ToHtml(model: Object) {
            this._$item.BindObject(model);
        };


        public SetObject(obj: Object) {
            this.CurrentItem = obj;
            this.ToHtml(this.CurrentItem);
        }

    }
}